#include "vl_generate_support.h"
#include "utils/vl_utils.h"

using namespace VL;


/**
 * @Author: CimZzz
 * @Date: 2022-08-19 16:05:13
 * 
 * 构造方法
 */
GenerateSupport::GenerateSupport(): VLSupport(VLSupport::Aspect::Generate) {

}

/**
 * @Author: CimZzz
 * @Date: 2022-08-19 15:53:54
 * 
 * 执行动作
 * 
 * @param env 环境变量
 */
void GenerateSupport::doAction(VLEnv* env) {
	// 获取生成参数
	auto genParams = env->generateParams;
	if(genParams.get() == nullptr) {
		this->throwException("generate params not found");
	}

	auto dir = env->projectDir.get();
	auto name = genParams->name;
	auto generateType = genParams->generateType;

	if(generateType == GenerateParams::None) {
		this->throwException("must specify generate type");
	}

	if(name == "") {
		this->throwException("must specify project name or file name");
	}

	if(dir == nullptr) {
		this->throwException("must specify generate dir");
	}

	if(generateType == GenerateParams::Toolchain) {
		// 生成工具链文件
		this->generateToolchainFile(name, dir);
		return;
	}

	if(generateType == GenerateParams::Class) {
		// 生成类文件
		this->generateClassFile(name, dir);
		return;
	}

	// 检查项目是否存在
	env->projectDir = std::shared_ptr<VLFile>(new VLFile(*env->projectDir, genParams->name));
	if(this->initProject(env)) {
		// 项目存在, 抛出异常中断
		this->throwException("project is exist");
	}

	// 生成通用项目结构
	this->generateCommonProjectStructure(env);

	if(generateType == GenerateParams::Exec) {
		// 生成可执行项目细节部分
		this->generateExecProject(env);
	} else {
		// 生成库项目细节部分
		this->generateLibraryProject(env);
	}
}


/**
 * @Author: CimZzz
 * @Date: 2022-08-19 16:12:55
 * 
 * 生成工具链文件
 * 
 * @param name 文件名
 * @param dir 生成文件所在目录
 */
void GenerateSupport::generateToolchainFile(const std::string& name, const VLFile* dir) {
	VLFile toolchainFile(*dir, name + ".toolchain");

	FILE_CREATE_AND_OUTPUT(toolchainFile, {
		output << "# C Compiler \n";
		output << "CC: \n";
		output << "\n";
		output << "# C++ Compiler \n";
		output << "CXX: \n";
		output << "\n";
		output << "# Archive \n";
		output << "AR: \n";
		output << "\n";
		output << "# RANLIB \n";
		output << "RANLIB: \n";
		output << "\n";
		output << "# System Root \n";
		output << "SYSROOT: \n";
		output << "\n";
	})
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-19 16:15:48
 * 
 * 生成类文件
 * 
 * @param name 文件名
 * @param dir 生成文件所在目录指针
 */
void GenerateSupport::generateClassFile(const std::string& name, const VLFile* dir) {
	VLFile sourceFile(*dir, name + ".cpp");
	VLFile sourceHeaderFile(*dir, name + ".h");
	
	// {project name}.cpp
	FILE_CREATE_AND_OUTPUT(sourceFile, {
		output << "#include \"" << name << ".h\"\n";
	})
	
	std::string upperStr = VLUtils::toUpperStr(name);
	upperStr = "_" + upperStr + "__H_";

	// {project name}.h
	FILE_CREATE_AND_OUTPUT(sourceHeaderFile, {
		output << "#ifndef " << upperStr << "\n";
		output << "#define " << upperStr << "\n";
		output << "\n";
		output << "\n";
		output << "#endif // " << upperStr << "\n";
	})
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-19 16:17:59
 * 
 * 生成通用的项目结构
 * 
 * @param env 环境变量
 */
void GenerateSupport::generateCommonProjectStructure(VLEnv* env) {
	// Virtual - Lightning 风格项目基本目录结构
	// - README.md       : read me 文件
	// - LICENSE         : 证书文件
	// - CHANGELOG.md    : 更新记录
	// - vlproject.vlc   : vl 项目工程文件 (在本方法中不生成)
	// - vlconfig.vlp    : vl 项目配置文件 (在本方法中不生成)
	// - .vl_build       : 工程构建目录    (在本方法中不生成)
	// - .gitignore      : git 忽略文件
	// - src             : 源码目录
	// - test            : 测试用例源码目录
	std::shared_ptr<ProjectProperties> projectProperties = env->projectProperties;

	// 创建项目文件夹
	VLEnv::newDir(&projectProperties->projectDir);

	VLFile readme(projectProperties->projectDir, "README.md");
	VLFile license(projectProperties->projectDir, "LICENSE");
	VLFile changeLog(projectProperties->projectDir, "CHANGELOG.md");
	VLFile gitignore(projectProperties->projectDir, ".gitignore");
	VLFile srcDir = projectProperties->srcDir;
	VLFile testDir = projectProperties->testDir;

	VLEnv::newFile(&readme);
	VLEnv::newFile(&license);
	VLEnv::newFile(&changeLog);
	VLEnv::newDir(&srcDir);
	VLEnv::newDir(&testDir);

	FILE_CREATE_AND_OUTPUT(gitignore, {
		output << ".vl_build\n";
		// 忽略 .vscode 工程目录文件
		output << ".vscode\n";
	})
}



/**
 * @Author: CimZzz
 * @Date: 2022-08-04 11:42:39
 * 
 * 生成可执行项目工程
 * 
 * @param env 环境变量
 */
void GenerateSupport::generateExecProject(VLEnv* env) {
	auto generateParam = env->generateParams;
	auto projectProperties = env->projectProperties;
	std::string name = generateParam->name;


	// 构建源码文件
	VLFile srcFile(projectProperties->srcDir, name + ".cpp");
	FILE_CREATE_AND_OUTPUT(srcFile, {
		output << "#include <cstdio>\n";
		output << "\n";
		output << "int main(int argc, const char ** argv) {\n";
		output << "\tprintf(\"hello world\\n\");\n";
		output << "\treturn 0;\n";
		output << "}\n";
	})

	// vlconfig.vlp
	FILE_CREATE_AND_OUTPUT(projectProperties->configFile, {
		output << "# Project Name \n";
		output << "Name: " + name + "\n";
		output << "# Version \n";
		output << "VERSION: 1.0.0\n";
		output << "# C Pre Processing flags \n";
		output << "CPPFLAGS: \n";
		output << "\n";
		output << "# C ++ Compiler flags, e.g. -std=c++11 \n";
		output << "CXXFLAGS: \n";
		output << "\n";
		output << "# C Compiler flags \n";
		output << "CFLAGS: \n";
		output << "\n";
		output << "# Link flags \n";
		output << "LDFLAGS: \n";
		output << "\n";
		output << "# Link VL Style Project, from network(git) \n";
		output << "# struct like this: \n";
		output << "# VL_LIBS: \n";
		output << "# 	[LibraryName]: \n";
		output << "# 		method: git\n";
		output << "# 		path: [LibraryPath]\n";
		output << "# 		branch: master\n";
		output << "# * only support git right now. \"branch\" is optional setting\n";
		output << "VL_LIBS: \n";
		output << "\n";
		output << "# Desribe test unit at here\n";
		output << "TEST:\n";
		output << "\n";
		output << "# Desribe programs at here\n";
		output << "PROGRAMS:\n";
		output << "\t" << name << ": \n";
		output << "\t\tVL_LIBS: \n";
		output << "\t\tLDFLAGS: \n";
		output << "\t\tSOURCES: \n";
		output << "\t\t\t - " << name << ".cpp\n";
		output << "\n";
	})

	RootProp projectProp;
	projectProp("version", VLEnv::VERSION);
	if(!projectProp.writeToVLCFile(projectProperties->projectFile)) {
		this->throwException("write project file failed: %s", projectProperties->projectFile.getAbsolutePath().c_str());
	}
}


/**
 * @Author: CimZzz
 * @Date: 2022-08-04 11:43:19
 * 
 * 生成库项目工程
 * 
 * @param env 环境变量
 */
void GenerateSupport::generateLibraryProject(VLEnv* env) {
	auto generateParam = env->generateParams;
	std::string name = generateParam->name;
	auto projectProperties = env->projectProperties;

	this->generateClassFile(name, &projectProperties->srcDir);

	// 构建测试文件
	VLFile testFile(projectProperties->testDir, "test.cpp");
	FILE_CREATE_AND_OUTPUT(testFile, {
		output << "#include <cstdio>\n";
		output << "#include \"" << name << ".h\"\n";
		output << "\n";
		output << "int main(int argc, const char ** argv) {\n";
		output << "\tprintf(\"hello world\\n\");\n";
		output << "\treturn 0;\n";
		output << "}\n";
	})

	// vlconfig.vlp
	FILE_CREATE_AND_OUTPUT(projectProperties->configFile, {
		output << "# Project Name \n";
		output << "Name: " + name + "\n";
		output << "# Version \n";
		output << "VERSION: 1.0.0\n";
		output << "# C Pre Processing flags \n";
		output << "CPPFLAGS: \n";
		output << "\n";
		output << "# C ++ Compiler flags, e.g. -std=c++11 \n";
		output << "CXXFLAGS: \n";
		output << "\n";
		output << "# C Compiler flags \n";
		output << "CFLAGS: \n";
		output << "\n";
		output << "# Link flags \n";
		output << "LDFLAGS: \n";
		output << "\n";
		output << "# Link VL Style Project, from network(git) \n";
		output << "# struct like this: \n";
		output << "# VL_LIBS: \n";
		output << "# 	[LibraryName]: \n";
		output << "# 		method: git\n";
		output << "# 		path: [LibraryPath]\n";
		output << "# 		branch: master\n";
		output << "# * only support git right now. \"branch\" is optional setting\n";
		output << "VL_LIBS: \n";
		output << "\n";
		output << "# Desribe test unit at here\n";
		output << "TEST:\n";
		output << "\t" << "test" << ": \n";
		output << "\t\tDESC: \n";
		output << "\t\tVL_LIBS: \n";
		output << "\t\tCOMPILE_FLAGS: \n";
		output << "\t\tENABLE: true \n";
		output << "\t\tCHECK: false \n";
		output << "\t\tENTRY: test.cpp \n";
		output << "\t\tPARAMS: \n";
		output << "\t\tCPPFLAGS: \n";
		output << "\t\tLDFLAGS: \n";
		output << "\t\tSOURCES: \n";
		output << "\t\t\t - " << name << ".cpp\n";
		output << "\n";
		output << "# Desribe libraries at here\n";
		output << "LIBRARIES:\n";
		output << "\n";
		output << "# Desribe libraries at here\n";
		output << "LIBRARIES:\n";
		output << "\t" << name << ": \n";
		output << "\t\tVL_LIBS: \n";
		output << "\t\tLDFLAGS: \n";
		output << "\t\tHEADERS: \n";
		output << "\t\t\t - " << name << ".h\n";
		output << "\t\tSOURCES: \n";
		output << "\t\t\t - " << name << ".cpp\n";
		output << "\n";
	})

	RootProp projectProp;
	projectProp("version", VLEnv::VERSION);
	if(!projectProp.writeToVLCFile(projectProperties->projectFile)) {
		this->throwException("write project file failed: %s", projectProperties->projectFile.getAbsolutePath().c_str());
	}
}
